#include <iostream>
#include <chrono>
#include <thread>
#include <deque>

#include "coroutine.h"
#include "log.h"
#include <csignal>

void init_signal(){
    sigset_t empty_set;
    sigemptyset(&empty_set);

    struct sigaction sa{};

    auto sig_handler = [](int signo) {
        LOGI << "Got signal " << signo << ", exit now...";
        exit(0);
    };
    sa.sa_handler = sig_handler;
    sa.sa_mask = empty_set;
    sa.sa_flags = SA_SIGINFO;

    sigaction(SIGINT, &sa, nullptr);
    sigaction(SIGTERM, &sa, nullptr);

}

int main(int argc, const char *argv[]) {
//    setLevel(LogLevel::DEBUG);
    auto sch = std::make_unique<Schedule>();
    std::deque<int> box;
    static int SIZE = 3;
    static int PRODUCTS_NUM = 100;

    using std::chrono_literals::operator ""ms;
    using std::chrono_literals::operator ""s;

    init_signal();

    auto producer = sch->addCoroutine([&](Coroutine *co) -> int {
//        auto param = static_cast<const char *>(co->getUserData());
        for (auto i = 0; i < PRODUCTS_NUM;) {
            if (box.size() >= SIZE) {
//                LOGI << "Box full, producer " << co->getId() << " rest now...";
                co->yield();
                continue;
            }
            LOGI << "Producer " << co->getId() << " produce " << i;
            box.push_back(i++);
            std::this_thread::sleep_for(500ms);
//            co->yield();
        }
        LOGI << "Producer " << co->getId() << " job done, go free now...";
        return 0;
    }, "user data here");

    auto consumer = sch->addCoroutine([&](Coroutine *co) -> int {
        for (auto i = 0; i < PRODUCTS_NUM;) {
            if (box.empty()) {
//                LOGI << "No more products, consumer " << co->getId() << " rest now...";
                co->yield();
                continue;
            }
            auto p = box.front();
            box.pop_front();
            LOGI << "Consumer " << co->getId() << " consume " << p;
            ++i;
            std::this_thread::sleep_for(200ms);
//            co->yield();
        }
        LOGI << "Consumer " << co->getId() << " job done, go free now...";
        return 0;
    }, "I am coroutine 2");

    int i = 0;
    auto currentCo = producer;
    while (sch->hasCoroutine()) {
        currentCo->resume();
        currentCo = currentCo == producer ? consumer : producer;
        i++;
    }
    LOGI << "Main function exit...";

    return 0;
}
